<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Human Resource Machine</title>
    <meta charset="utf-8" />
    <link rel="stylesheet" type="text/css" href="../css/index.css">
    <script src="../js/jquery-3.2.1.min.js"></script>
    <script src="../js/vue.min.js"></script>
    <script src="../js/Sortable.min.js"></script>
    <script src="../js/vuedraggable.umd.min.js"></script>
    <script src="../js/ScrollToBySpeed.min.js"></script>
</head>

<body>
    <div id="app">
        <div class="main">
            <div class="left" style="display: none;">
                <h2>TYPE IN</h2>
                <input id="in" type="text" placeholder="输入" v-model="input" />
                <input id="place" type="text" placeholder="场景" v-model="place" />
                <textarea id="code" rows="30" cols="20" placeholder="程序" v-model="code"></textarea>
                <input class="btn" type="button" @click="run()" value="运行" />
                <textarea id="out" rows="5" cols="20" placeholder="输出" v-model="output"></textarea>
            </div>
            <div class="middle">
                <div class="board" style="display: flex;">
                    <div class="input">
                        <div
                            class="cell in-box"
                            :class="valType(inbox)"
                            v-for="(inbox, inbox_index) in inputArr"
                            :key="inbox_index"
                            :style="`top: ${20 + inbox_index * 45}px;`"
                        >
                            <b>{{ inbox }}</b>
                        </div>
                    </div>
                    <div class="scene">
                        <!-- <div
                            class="player"
                            :style="`
                                top: ${playerPosition.top}px;
                                left: ${playerPosition.left}px;
                                transition: ${gameState === 'pre' ? 'none' : 'all ' + executeDuration / 2 + 'ms'};
                            `"
                        ></div> -->
                        <div
                            id="ghost"
                            class="ghost player"
                            :style="`
                                top: ${playerPosition.top}px;
                                left: ${playerPosition.left}px;
                                transition: ${gameState === 'pre' ? 'none' : 'all ' + executeDuration / 2 + 'ms'};
                            `"
                        >
                            <div class="ghost__head">
                                <div class="ghost__eyes"></div>
                                <div class="ghost__mouth"></div>
                            </div>
                            <div class="ghost__tail">
                                <div class="ghost__rip"></div>
                            </div>
                        </div>
                        <svg xmlns="https://www.w3.org/2000/svg" version="1.1" style="display: none;">
                            <defs>
                                <filter id="goo">
                                    <feGaussianBlur in="SourceGraphic" stdDeviation="10" result="ghost-blur" />
                                    <feColorMatrix in="ghost-blur" mode="matrix" values="
                                        1 0 0 0 0
                                        0 1 0 0 0
                                        0 0 1 0 0
                                        0 0 0 16 -7" result="ghost-gooey" />
                                </filter>
                            </defs>
                        </svg>
                        <div
                            class="cell hand"
                            :class="valType(hand)"
                            :style="`
                                opacity: ${hand ? 1 : 0};
                                top: ${playerPosition.top - 20}px;
                                left: ${playerPosition.left + 7}px;
                                transition: ${gameState === 'pre' ? 'none' : 'top ' + executeDuration / 2 + 'ms, left ' + executeDuration / 2 + 'ms'};
                            `"
                        >
                            {{ hand }}
                        </div>
                        <div
                            class="cell-box"
                            v-for="(cell, cell_index) in scene"
                            :key="cell_index"
                            :style="`
                                cursor: ${ currCellIndex !== null ? 'pointer' : 'default' };
                            `"
                            @click="selectCell(cell, cell_index)"
                        >
                            <div
                                class="cell"
                                :class="valType(cell)"
                                :style="`
                                    opacity: ${cell ? 1 : 0};
                                    transition: ${gameState === 'pre' ? 'none' : 'all ' + executeDuration / 2 + 'ms'};
                                `"
                            >
                                {{ cell || '-' }}
                            </div>
                            <div class="corner-mark">{{ cell_index }}</div>
                        </div>
                        <div class="selected" v-if="currCellIndex!==null" :style="`left: ${currCellIndex % 5 * 75 - 5}px; top: ${parseInt(currCellIndex / 5) * 75 - 5}px`"></div>
                        <div class="btn-wrap">
                            <div
                                class="exec-btn stop-btn"
                                :class="`${gameState === 'stop' ? 'btn-disabled' : ''}`"
                                @click="cancelSelect();stop();"
                            >◼</div>
                            <div
                                class="exec-btn pre-btn"
                                :class="`${gameState === 'run' || currStepIndex === 0 ? 'btn-disabled' : ''}`"
                                @click="gameState === 'run' || currStepIndex === 0 ? null : preStep()"
                            >△</div>
                            <div
                                class="exec-btn run-btn"
                                :class="`${gameState === 'over' || (result && result.data && currStepIndex === result.data.length - 1) ? 'btn-disabled' : ''}`"
                                @click="autoRun()"
                            >{{ gameState !== 'run' ? '▼' : '〓' }}</div>
                            <div
                                class="exec-btn next-btn"
                                :class="`${gameState === 'run' || (result && result.data && currStepIndex === result.data.length - 1) ? 'btn-disabled' : ''}`"
                                @click="gameState === 'run' || (result && result.data && currStepIndex === result.data.length - 1) ? null : nextStep()"
                            >▽</div>
                            <div style="float:right;width: 120px;margin-left: 20px;margin-top: 10px;">
                                <input type="range" min="1" max="5" step="0.1" v-model="executionSpeed" />
                            </div>
                        </div>
                    </div>
                    <div class="output">
                        <div
                            class="cell in-box"
                            :class="valType(outbox)"
                            v-for="(outbox, outbox_index) in outputArr"
                            :key="outbox_index"
                            :style="`top: ${20 + outbox_index * 45}px;`"
                        >
                            <b>{{ outbox }}</b>
                        </div>
                    </div>
                    <div class="base" @click="cancelSelect()">
                        <draggable
                            id="base-drag-wrap"
                            :list="baseCodeArr"
                            :group="{ name: 'codeList', pull: 'clone', put: putFn }"
                            :sort="false"
                            @end="baseDragEnd"
                        >
                            <div
                                class="command-line"
                                :class="commandType(command)"
                                v-for="(command, command_index) in baseCodeArr"
                                :key="command_index"
                                @drag="baseDragging"
                            >{{ commandCard(command) }}</div>
                        </draggable>
                        <div class="modal" :style="{ opacity: isReadyDel ? '0.2' : '0' }">♻</div>
                    </div>
                    <div class="answer" @click="cancelSelect()">
                        <div class="dropdown">
                            <div class="subject dropbtn">
                                <b>{{ examples.length > 0 ? examples[currSubjectIndex].title : '' }}</b>
                            </div>
                            <div class="dropdown-content">
                                <div v-for="(item, index) in examples" :key="index" @click="stageChange(index)">
                                    {{ item.title }}
                                </div>
                            </div>
                        </div>
                        <div id="command-wrap">
                            <div class="order">
                                <div
                                    ref="pointer"
                                    v-show="gameState !== 'stop' && currStepIndex > 0"
                                    class="pointer"
                                    :style="`top: ${8 + 38 * currPointIndex}px;transition: top ${executeDuration}ms;`">
                                    ▶
                                </div>
                                <div
                                    :id="`order-number-${code_index}`"
                                    class="order-number"
                                    v-for="(code, code_index) in codeArr"
                                    :key="code_index"
                                >
                                    {{ codeOrder(code, code_index) }}
                                </div>
                            </div>
                            <draggable
                                id="drag-wrap"
                                :list="codeArr"
                                :group="{ name: 'codeList' }"
                                v-bind="dragOptions"
                                @end="dragEnd"
                            >
                                <transition-group class="transition" type="transition" name="flip-list">
                                    <div
                                        class="command-line"
                                        :class="commandType(command)"
                                        :id="uid(command, command_index)"
                                        v-for="(command, command_index) in codeArr"
                                        :key="command_index"
                                        @drag="dragging"
                                        @click="selectCommandLine(command, command_index)"
                                    >
                                        <span>{{ commandCard(command) }}</span>
                                        <div v-if="currCommandLineIndex === command_index" style="position: absolute;right: 0;top: 0;" @click.stop>
                                            <input
                                                type="checkbox"
                                                id="toggle-button"
                                                name="switch"
                                                v-model="hasBracket(command)[0]"
                                                @change="switchChange(command_index)"
                                            >
                                            <label for="toggle-button" class="button-label">
                                                <span class="circle"></span>
                                            </label>
                                        </div>
                                    </div>
                                </transition-group>
                            </draggable>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
<script>
    new Vue({
        el: '#app',
        data: {
            result: null,
            isReadyDel: false, // 准备删除
            baseCodeArr: ['INBOX', 'OUTBOX', 'COPYFROM', 'COPYTO', 'ADD', 'SUB', 'BUMPUP', 'BUMPDN', 'JUMP', 'JUMPZ', 'JUMPN'],
            codeArr: [],
            input: '', // 输入
            inputArr: [],
            place: '', // 场景
            code: '', // 代码
            output: '', // 输出
            outputArr: [],
            hand: '',
            examples: [], // 例子
            scene: [
                '', '', '', '', '', 
                '', '', '', '', '', 
                '', '', '', '', '', 
                '', '', '', '', '', 
                '', '', '', '', '', 
                '', '', '', '', ''
            ],
            currCommandLineIndex: null,
            currCellIndex: null,
            currSubjectIndex: 0, // 当前关卡的索引
            currPointIndex: -1, // 当前指针的索引
            currStepIndex: -1, // 当前步骤的索引
            stepInterval: null,
            gameState: 'stop',
            playerPosition: {
                top: -70,
                left: 0
            },
            executionSpeed: 1 // 执行倍速
        },
        components: {
            draggable: vuedraggable
        },
        computed: {
            executeDuration() {
                const baseDuration = 800
                return baseDuration / this.executionSpeed
            },
            dragOptions() {
                return {
                    animation: 200,
                    group: 'description',
                    disabled: false,
                    ghostClass: 'ghost'
                }
            }
        },
        watch: {
            code(val) {
                if (val) {
                    this.codeArr = val.split('\n').filter(v => !!v)
                } else {
                    this.codeArr = []
                }
                this.sortChange()
            },
            codeArr(val) {
                this.code = val.join('\n')
                this.sortChange()
            },
            output(val) {
                if (val) {
                    this.outputArr = val.split(',').filter(v => !!v)
                } else {
                    this.outputArr = []
                }
            },
            outputArr(val) {
                this.output = val.join(',')
            },
            currPointIndex(val) {
                const answer = $('.answer')[0]
                const pointerTop = 8 + 38 * val
                const wrapHeight = answer.clientHeight - 70 - $('.subject')[0].clientHeight
                const scrollTop = answer.scrollTop
                if (pointerTop > wrapHeight + scrollTop) {
                    answer.scrollTo({ top: answer.scrollTop + 1 }) // 防止出现首次不滚动bug
                    scrollToBySpeed(answer, pointerTop - wrapHeight, this.executeDuration / 2)
                } else if (pointerTop < scrollTop) {
                    scrollToBySpeed(answer, pointerTop, this.executeDuration / 2)
                }
            },
            async playerPosition(val, oldVal) {
                const map = function (num, in_min, in_max, out_min, out_max) {
                    return (num - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
                }
                const skewX = map(val.left, 0, 50, 0, -25)
                const el = $('#ghost')[0]
                const diff = oldVal.left - val.left
                const sign = diff / Math.abs(diff)
                const deg = sign * Math.pow(Math.abs(diff), 0.6)
                el.style.transform = `scale(0.5) skew(${deg}deg)`
                await this.sleep(this.executeDuration / 4)
                el.style.transform = `scale(0.5) skew(0deg)`
            }
        },
        mounted() {
            this.initExamples()
            this.stageChange(this.currSubjectIndex)
        },
        methods: {
            // 略过跳转结束命令行后的代码序号
            codeOrder(code, code_index) {
                const emptyIndexes = []
                this.codeArr.map((v, i) => { ~v.indexOf(':') && emptyIndexes.push(i) })
                let n = code_index - 1 * (emptyIndexes.filter(v => { return v < code_index }).length) + 1 + ''
                return !~code.indexOf(':') ? n.padStart(2, '0') : ''
            },
            // 开关切换
            switchChange(index) {
                const val = this.codeArr[index].split(' ')
                const regex = /\[(.+?)\]/
                if (~val[1].indexOf('[')) {
                    this.codeArr.splice(index, 1, `${this.codeArr[index].split(' ')[0]} ${regex.exec(val)[1]}`)
                } else {
                    this.codeArr.splice(index, 1, `${this.codeArr[index].split(' ')[0]} [${this.codeArr[index].split(' ')[1]}]`)
                }
            },
            // 是否有中括号
            hasBracket(command) {
                return [~command.indexOf('['), command.split(' ')[1]]
            },
            // 切换关卡
            stageChange(index) {
                this.stop() // 停止当前的程序
                this.inputArr = this.examples[index].input
                this.scene = this.examples[index].place
                this.codeArr = this.examples[index].code
                this.currSubjectIndex = index
                this.set(index)
                $('.dropdown-content')[0].style.pointerEvents = 'none'
                setTimeout(() => {
                    $('.dropdown-content')[0].style.pointerEvents = ''
                }, this.executeDuration / 2) // 要大于动画时间0.1s
            },
            // 取消选择命令行
            cancelSelect() {
                for (let i = 0; i < $('.command-line').length; i++) {
                    $('.command-line')[i].style.width = '150px'
                    $('.command-line')[i].style.height = '30px'
                    $('.command-line')[i].style.lineHeight = '30px'
                }
                for (let i = 0; i < $('.order-number').length; i++) {
                    $('.order-number')[i].style.height = '30px'
                    $('.order-number')[i].style.lineHeight = '30px'
                }
                this.currCommandLineIndex = null
                this.currCellIndex = null
                this.sortChange() // 重置svg位置
            },
            // 点击选择格子
            selectCell(cell, cell_index) {
                if (this.currCommandLineIndex !== null) {
                    this.currCellIndex = cell_index
                    const val = this.codeArr[this.currCommandLineIndex].split(' ')
                    if (~val[1].indexOf('[')) {
                        this.codeArr[this.currCommandLineIndex] = `${val[0]} [${cell_index}]`
                    } else {
                        this.codeArr[this.currCommandLineIndex] = `${val[0]} ${cell_index}`
                    }
                }
            },
            // 点击选择命令行
            selectCommandLine(command, command_index) {
                if (!command) {
                    return
                }
                setTimeout(() => {
                    // 获取当前命令行
                    if (~['COPYFROM', 'COPYTO', 'ADD', 'SUB', 'BUMPUP', 'BUMPDN'].indexOf(command.split(' ')[0])) {
                        $(`#c-${command_index}`)[0].style.width = '200px'
                        $(`#c-${command_index}`)[0].style.height = '45px'
                        $(`#c-${command_index}`)[0].style.lineHeight = '45px'
                        $(`#order-number-${command_index}`)[0].style.height = '45px'
                        $(`#order-number-${command_index}`)[0].style.lineHeight = '45px'
                        this.currCommandLineIndex = command_index
                        const regex = /\[(.+?)\]/
                        const val = command.split(' ')[1]
                        this.currCellIndex = ~val.indexOf('[') ? regex.exec(val)[1] : val
                    } else {
                        this.currCommandLineIndex = null
                        this.currCellIndex = null
                    }
                    // 获取当前跳转曲线
                    let letter = ''
                    if (~command.indexOf('JUMP')) {
                        letter = command.split(' ')[1]
                    } else if (~command.indexOf(':')) {
                        letter = command.split(':')[0]
                    }
                    setTimeout(() => {
                        letter && $(`#line-${letter}`)[0].setAttribute('stroke', 'rgb(158, 154, 209)')
                        letter && $(`#arrow-path-${letter}`)[0].setAttribute('fill', 'rgb(158, 154, 209)')
                    }, 0)
                    this.sortChange() // 重置svg位置
                }, 0)
            },
            // 创建一个新字母
            createLetter() {
                let maxLetter = 96 // a-z 97-122
                this.codeArr.map(v => {
                    if (~v.indexOf(':')) {
                        let oldMax = v.split(':')[0].charCodeAt()
                        maxLetter = oldMax > maxLetter ? oldMax : maxLetter
                    }
                })
                maxLetter++
                return String.fromCharCode(maxLetter)
            },
            // base drag end
            baseDragEnd(e) {
                if (e.to.id === 'base-drag-wrap') {
                    // return
                } else if (this.isReadyDel) { // 插入后移除
                    this.$nextTick(() => {
                        this.isReadyDel = false
                        this.codeArr.splice(e.newIndex, 1)
                    })
                } else if (!this.isReadyDel) { // 插入
                    this.$nextTick(() => {
                        if (~this.codeArr[e.newIndex].indexOf('JUMP')) {
                            const letter = this.createLetter()
                            this.codeArr.splice(e.newIndex, 1, `${this.codeArr[e.newIndex]} ${letter}`)
                            this.codeArr.splice(e.newIndex, 0, `${letter}:`)
                        } else if (~['COPYFROM', 'COPYTO', 'ADD', 'SUB', 'BUMPUP', 'BUMPDN'].indexOf(this.codeArr[e.newIndex])) {
                            this.codeArr.splice(e.newIndex, 1, `${this.codeArr[e.newIndex]} 0`)
                            this.selectCommandLine(this.codeArr[e.newIndex], e.newIndex)
                        }
                    })
                }
                this.cancelSelect()
                this.isReadyDel = false
            },
            // drag end
            dragEnd(e) {
                if (this.isReadyDel) {
                    if (~e.clone.id.indexOf('start-') || ~e.clone.id.indexOf('end-')) { // is jump
                        const letter = e.clone.id.split('-')[1]
                        this.codeArr = this.codeArr.filter(v => { return v !== `${letter}:` && v !== `JUMP ${letter}` && v !== `JUMPZ ${letter}` && v !== `JUMPN ${letter}` })
                    } else {
                        this.codeArr.splice(e.newIndex, 1)
                    }
                }
                this.isReadyDel = false
                this.cancelSelect()
            },
            // 获取是否丢弃状态
            putFn(e) {
                if (e.el.id === 'base-drag-wrap') {
                    this.isReadyDel = true
                }
                return false
            },
            // 命令行卡片显示内容
            commandCard(command) {
                if (~command.indexOf('BUMPUP')) {
                    return `BUMP+ ${command.split(' ')[1] || ''}`
                } else if (~command.indexOf('BUMPDN')) {
                    return `BUMP- ${command.split(' ')[1] || ''}`
                } else if (~command.indexOf('JUMPZ')) {
                    return `JUMP ${command.split(' ')[1] || '' } (if = 0)`
                } else if (~command.indexOf('JUMPN')) {
                    return `JUMP ${command.split(' ')[1] || '' } (if < 0)`
                } else {
                    return command
                }
            },
            // base拖拽中
            baseDragging(e) {
                if (e.path.find(v => v.id === 'drag-wrap')) {
                    this.isReadyDel = false
                } else if (e.path.find(v => v.id === 'base-drag-wrap')) {
                    this.isReadyDel = true
                }
                this.sortChange()
            },
            // 拖拽中
            dragging() {
                this.isReadyDel = false
                this.sortChange()
            },
            // 顺序变化
            sortChange() {
                this.$nextTick(() => {
                    this.stop()
                    $(`#command-wrap`).children(".svg").remove()
                    let count = 0
                    this.codeArr.map(line => {
                        if (~line.indexOf(':')) {
                            count++
                            const jumpNode = line.split(':')[0]
                            if (!$(`#start-${jumpNode}`)[0]) return // 防止先插入end再插入start时，获取不到start
                            const start = { x: 0, y: $(`#start-${jumpNode}`)[0].offsetTop + 6 - 10 }
                            const end = { x: 15, y: $(`#end-${jumpNode}`)[0].offsetTop + 6 - 10 }
                            const mid = { x: 60 + count * 10, y: $(`#start-${jumpNode}`)[0].offsetTop + ($(`#end-${jumpNode}`)[0].offsetTop - $(`#start-${jumpNode}`)[0].offsetTop) / 2 + 6 - 10 }
                            const color = `rgb(131, 131, 183)`
                            $(`
                                <svg id="svg-${jumpNode}" class="svg" style="position:absolute;left: 230px;flex:1;height: calc(100% - 30px);" width="205px">
                                    <defs>
                                        <marker id="arrow-${jumpNode}" markerWidth="6" markerHeight="9" refx="2" refy="6" orient="auto">
                                            <path id="arrow-path-${jumpNode}" d="M0,3 L1,6 L0,9 L6,6" fill="${color}" style="transition:all 0.2s;" />
                                        </marker>
                                        <filter id="shadow-${jumpNode}" width="200%" height="200%" x="0" y="-0.5">
                                            <feOffset result="offOut" in="SourceAlpha" dx="2" dy="2" />
                                            <feGaussianBlur result="blurOut" in="offOut" stdDeviation="3" />
                                            <feBlend in="SourceGraphic" in2="blurOut" mode="normal" />
                                        </filter>
                                    </defs>
                                    <path
                                        id="line-${jumpNode}"
                                        marker-end="url(#arrow-${jumpNode})"
                                        d="M ${start.x} ${start.y} Q ${mid.x} ${start.y} ${mid.x} ${mid.y} Q ${mid.x} ${end.y} ${end.x} ${end.y}"
                                        stroke="${color}"
                                        fill="none"
                                        stroke-width="4"
                                        filter="url(#shadow-${jumpNode})"
                                        style="transition:all 0.2s;"
                                    />
                                </svg>
                            `).appendTo($(`#command-wrap`))
                        }
                    })
                })
            },
            // 生成跳转唯一id
            uid(val, index) {
                if (~val.indexOf(':')) {
                    return `end-${val.split(':')[0]}`
                } else if (~val.indexOf('JUMP')) {
                    return `start-${val.split(' ')[1]}`
                } else {
                    return `c-${index}`
                }
            },
            // command 类型
            commandType(command) {
                if (~['INBOX', 'OUTBOX'].indexOf(command)) {
                    return 'green'
                } else if (~command.indexOf('COPYFROM') || ~command.indexOf('COPYTO')) {
                    return 'red'
                } else if (~command.indexOf('ADD') || ~command.indexOf('SUB') || ~command.indexOf('BUMPUP') || ~command.indexOf('BUMPDN')) {
                    return 'orange'
                } else {
                    return 'purple'
                }
            },
            // val类型
            valType(val) {
                if (val.toString().match(/^.*[A-Z]+.*$/)) {
                    return 'purple'
                } else if (val.toString().match(/^.*[0-9]+.*$/)) {
                    return 'green'
                } else {
                    return ''
                }
            },
            // 应用例子
            set(i) {
                this.input = this.examples[i].input.join(',')
                this.code = this.examples[i].code.join('\n')
                this.place = this.examples[i].place.join(',')
                this.output = ''
            },
            // 设置人物的位置
            setPlayerPosition(index) {
                if (~this.codeArr[index].indexOf('INBOX')) {
                    this.playerPosition = {
                        top: -50,
                        left: -75
                    }
                } else if (~this.codeArr[index].indexOf('OUTBOX')) {
                    this.playerPosition = {
                        top: -50,
                        left: 405
                    }
                } else if (~['COPYFROM', 'COPYTO', 'ADD', 'SUB', 'BUMPUP', 'BUMPDN'].indexOf(this.codeArr[index].split(' ')[0])) {
                    const val = this.codeArr[index].split(' ')
                    const regex = /\[(.+?)\]/
                    let order
                    if (~val[1].indexOf('[')) {
                        order = Number(this.scene[regex.exec(val)[1]])
                    } else {
                        order = val[1]
                    }
                    const x = parseInt(order / 5)
                    const y = order % 5
                    this.playerPosition = {
                        top: -20 + 75 * x,
                        left: 10 + 75 * y
                    }
                }
            },
            // 同步延时函数
            sleep(duration) {
                return new Promise((resolve) => {
                    setTimeout(() => {
                        resolve()
                    }, duration)
                })
            },
            // 上一步
            preStep() {
                this.gameState = 'pre'
                if (this.currStepIndex === 0) return
                this.hand = this.result.data[this.currStepIndex].hand
                this.output = this.result.data[this.currStepIndex].outbox.split(',').reverse().join(',')
                this.inputArr = this.result.data[this.currStepIndex].inbox ? this.result.data[this.currStepIndex].inbox.split(',') : []
                this.scene = this.result.data[this.currStepIndex].p.split(',')
                this.currStepIndex--
                this.currPointIndex = this.result.data[this.currStepIndex].index
                this.setPlayerPosition(this.result.data[this.currStepIndex].index)
            },
            // 下一步
            async nextStep() {
                if (this.gameState === 'stop') {
                    this.gameState = 'next' // 防止异步函数结果延时，状态未及时更新
                    await this.getData()
                }
                this.gameState = 'next'
                const len = this.result.data.length
                if (this.currStepIndex >= len - 1) return 
                this.currPointIndex = this.result.data[this.currStepIndex].index
                this.setPlayerPosition(this.result.data[this.currStepIndex].index)
                this.currStepIndex++
                await this.sleep(this.executeDuration / 2)
                this.hand = this.result.data[this.currStepIndex].hand
                this.output = this.result.data[this.currStepIndex].outbox.split(',').reverse().join(',')
                this.inputArr = this.result.data[this.currStepIndex].inbox ? this.result.data[this.currStepIndex].inbox.split(',') : []
                this.scene = this.result.data[this.currStepIndex].p.split(',')
            },
            // 自动运行
            async autoRun() {
                if (this.gameState === 'run') {
                    this.gameState = 'pause'
                    return
                }
                this.cancelSelect()
                await this.getData()
                this.gameState = 'run'
                const len = this.result.data.length
                const fn = async () => {
                    this.currPointIndex = this.result.data[this.currStepIndex].index
                    this.setPlayerPosition(this.result.data[this.currStepIndex].index)
                    await this.sleep(this.executeDuration / 2)
                    if (this.currStepIndex >= len - 1) {
                        this.gameState = 'over'
                        if (this.result.code !== 200) {
                            setTimeout(() => {
                                alert('error:' + this.result.message)
                            }, 500)
                        }
                    } else {
                        this.currStepIndex++
                        this.hand = this.result.data[this.currStepIndex].hand
                        this.output = this.result.data[this.currStepIndex].outbox.split(',').reverse().join(',')
                        this.inputArr = this.result.data[this.currStepIndex].inbox ? this.result.data[this.currStepIndex].inbox.split(',') : []
                        this.scene = this.result.data[this.currStepIndex].p.split(',')
                    }
                }
                const _this = this
                loop = function () {
                    if (_this.gameState === 'run') {
                        fn()
                        setTimeout(loop, _this.executeDuration)
                    }
                }
                loop()
            },
            // 停止运行
            stop() {
                this.gameState = 'stop'
                this.stepInterval = null
                this.hand = ''
                this.output = ''
                this.inputArr = this.examples[this.currSubjectIndex].input
                this.scene = this.examples[this.currSubjectIndex].place
                this.currPointIndex = 0
                this.currStepIndex = 0
                this.playerPosition = {
                    top: -70,
                    left: 0
                }
            },
            // 获取数据
            getData() {
                return new Promise((resolve, reject) => {
                    let param = {
                        input: this.input,
                        code: this.code,
                        place: this.place
                    }
                    $.ajax({
                        url: '../submit/CalcBase.ashx',
                        type: 'POST',
                        contentType: "application/json; charset=utf-8",
                        dataType: 'text',
                        data: JSON.stringify(param),
                        success: (data) => {
                            // console.log(JSON.parse(data))
                            this.result = JSON.parse(data)
                            resolve()
                        },
                        error: (data, err) => {
                            console.log(data, err)
                            reject()
                        }
                    })
                })
            },
            // 初始化例子
            initExamples() {
                this.examples = [
                    {
                        title: '【04】每2个颠倒顺序输出',
                        input: [3, 7, 'G', 'O', 3, 5],
                        place: [
                            '', '', '', '', '', 
                            '', '', '', '', '', 
                            '', '', '', '', '', 
                            '', '', '', '', '', 
                            '', '', '', '', '', 
                            '', '', '', '', ''
                        ],
                        code: [
                            'a:',
                            'INBOX',
                            'COPYTO 0',
                            'INBOX',
                            'OUTBOX',
                            'COPYFROM 0',
                            'OUTBOX',
                            'JUMP a'
                        ]
                    },
                    {
                        title: '【09】遇到0输出',
                        input: [5, 0, 5, 'E', 0, 0, 6],
                        place: [
                            '', '', '', '', '', 
                            '', '', '', '', '', 
                            '', '', '', '', '', 
                            '', '', '', '', '', 
                            '', '', '', '', '', 
                            '', '', '', '', ''
                        ],
                        code: [
                            'a:',
                            'b:',
                            'INBOX',
                            'JUMPZ c',
                            'JUMP b',
                            'c:',
                            'OUTBOX',
                            'JUMP a'
                        ]
                    },
                    {
                        title: '【22】斐波那契数列',
                        input: [5, 20],
                        place: [
                            '', '', '', '', '', 
                            '', '', '', '', '0', 
                            '', '', '', '', '', 
                            '', '', '', '', '', 
                            '', '', '', '', '', 
                            '', '', '', '', ''
                        ],
                        code: [
                            'a:',
                            'INBOX',
                            'COPYTO 0',
                            'COPYFROM 9',
                            'COPYTO 1',
                            'COPYTO 2',
                            'BUMPUP 1',
                            'OUTBOX',
                            'b:',
                            'COPYFROM 1',
                            'ADD 2',
                            'COPYTO 3',
                            'COPYFROM 0',
                            'SUB 3',
                            'JUMPN a',
                            'COPYFROM 3',
                            'OUTBOX',
                            'COPYFROM 1',
                            'COPYTO 2',
                            'COPYFROM 3',
                            'COPYTO 1',
                            'JUMP b'
                        ]
                    },
                    {
                        title: '【25】累加，如果输入是3，则输出3+2+1+0=6',
                        input: [4, 6, 0, 3],
                        place: [
                            '', '', '', '', '', 
                            '0', '', '', '', '', 
                            '', '', '', '', '', 
                            '', '', '', '', '', 
                            '', '', '', '', '', 
                            '', '', '', '', ''
                        ],
                        code: [
                            'JUMP c',
                            'a:',
                            'COPYFROM 0',
                            'b:',
                            'OUTBOX',
                            'c:',
                            'INBOX',
                            'JUMPZ b',
                            'COPYTO 0',
                            'COPYTO 2',
                            'd:',
                            'BUMPDN 2',
                            'JUMPZ a',
                            'ADD 0',
                            'COPYTO 0',
                            'JUMP d'
                        ]
                    },
                    {
                        title: '【28】每3个为一组从小到大输出',
                        input: [1, 2, -59, 5, -6, 0, 5, 5, 6, -5, 5, -6],
                        place: [
                            '', '', '', '', '', 
                            '', '', '', '', '', 
                            '', '', '', '', '', 
                            '', '', '', '', '', 
                            '', '', '', '', '', 
                            '', '', '', '', ''
                        ],
                        code: [
                            'h:',
                            'INBOX',
                            'COPYTO 0',
                            'INBOX',
                            'COPYTO 1',
                            'SUB 0',
                            'JUMPN a',
                            'COPYFROM 0',
                            'COPYTO 3',
                            'COPYFROM 1',
                            'COPYTO 4',
                            'JUMP b',
                            'a:',
                            'COPYFROM 1',
                            'COPYTO 3',
                            'COPYFROM 0',
                            'COPYTO 4',
                            'b:',
                            'INBOX',
                            'COPYTO 2',
                            'SUB 3',
                            'JUMPN c',
                            'COPYFROM 4',
                            'SUB 2',
                            'JUMPN e',
                            'COPYFROM 4',
                            'COPYTO 5',
                            'COPYFROM 2',
                            'COPYTO 4',
                            'JUMP g',
                            'e:',
                            'COPYFROM 2',
                            'COPYTO 5',
                            'g:',
                            'JUMP d',
                            'c:',
                            'COPYFROM 4',
                            'COPYTO 5',
                            'COPYFROM 3',
                            'COPYTO 4',
                            'COPYFROM 2',
                            'COPYTO 3',
                            'd:',
                            'COPYFROM 3',
                            'OUTBOX',
                            'COPYFROM 4',
                            'OUTBOX',
                            'COPYFROM 5',
                            'OUTBOX',
                            'JUMP h'
                        ]
                    },
                    {
                        title: '【29】输出数字对应位置的内容',
                        input: [0, 8, 2, 3, 0, 4, 1, 1],
                        place: [
                            'N', 'K', 'A', 'E', 'R', 
                            'D', 'O', 'L', 'Y', 'J', 
                            '', '', '', '', '', 
                            '', '', '', '', '', 
                            '', '', '', '', '', 
                            '', '', '', '', ''
                        ],
                        code: [
                            'a:',
                            'INBOX',
                            'COPYTO 10',
                            'COPYFROM [10]',
                            'OUTBOX',
                            'JUMP a'
                        ]
                    },
                    {
                        title: '【35】去重输出',
                        input: ['E', 'E', 'C', 'B', 'D', 'D', 'C'],
                        place: [
                            '', '', '', '', '', 
                            '', '', '', '', '', 
                            '', '', '', '', '0', 
                            '', '', '', '', '', 
                            '', '', '', '', '', 
                            '', '', '', '', ''
                        ],
                        code: [
                            'INBOX',
                            'COPYTO [14]',
                            'a:',
                            'OUTBOX',
                            'b:',
                            'INBOX',
                            'COPYTO 12',
                            'COPYFROM 14',
                            'COPYTO 13',
                            'c:',
                            'COPYFROM [13]',
                            'SUB 12',
                            'JUMPZ b',
                            'BUMPDN 13',
                            'JUMPN d',
                            'JUMP c',
                            'd:',
                            'BUMPUP 14',
                            'COPYFROM 12',
                            'COPYTO [14]',
                            'JUMP a'
                        ]
                    }
                ]
            }
        }
    })
</script>

</html>